/**                   GNU GENERAL PUBLIC LICENSE
 *                       Version 3, 29 June 2007
 *
 * Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 * Everyone is permitted to copy and distribute verbatim copies
 * of this license document, but changing it is not allowed.
 * See License.txt file that comes with this distribution
 *
 *
 * Author: Vitaly German <vitaly.german@gmail.com>, (C) 2007-2008
 * 
 **/

#pragma once

typedef double Numeric;

template <typename T>
std::string ToString(const T & t);

template <typename Node>
struct NodeFactory {
    typedef typename Node::parse_node_t pn_t;
    typedef typename Node::children_t ch_t;
    static Node MakeReal(double num) {
        std::string smult = ToString(num);
        Node node;
        node.value = pn_t(smult.begin(), smult.end());
        node.value.id(calculator::realID);
        return node;
    }
    static Node MakeMexpr(const Node & node1, const Node& node2, const std::string &mlt) {
          Node node;
          node.value = pn_t(mlt.begin(), mlt.end());
          node.value.id(calculator::mexprID);
          node.children.push_back(node1);
          node.children.push_back(node2);
          return node;
    }
    static Node MakeMath(const Node & node1, const Node& node2, const std::string & mlt) {
          Node node;
          node.value = pn_t(mlt.begin(), mlt.end());
          node.value.id(calculator::mathID);
          node.children.push_back(node1);
          node.children.push_back(node2);
          return node;
    }
    
    static Node Make2Mark(const Node & node1, const Node& node2, const MarkT & mrk) {
          Node node;
          node.value = pn_t(mrk.mark.begin(), mrk.mark.end());
          node.value.id(mrk.id);
          node.children.push_back(node1);
          node.children.push_back(node2);
          return node;
    }
    
    static Node Make1Mark(const Node & node1, const MarkT & mrk) {
          Node node;
          node.value = pn_t(mrk.mark.begin(), mrk.mark.end());
          node.value.id(mrk.id);
          node.children.push_back(node1);
          return node;
    }

};


template <class Node>
double DoubleFromNode(const Node& n){
   std::string sd(n.value.begin(), n.value.end());
   return strtod(sd.c_str(),0);
}

template <typename Node, typename Op>
bool eval(Node & node, Op op) {
   typedef typename Node::parse_node_t pn_t;
  assert(node.children.size()==2);
  if ( (node.children[0].value.id() == calculator::realID) &&
    (node.children[1].value.id() == calculator::realID))
    {
      double d1 = DoubleFromNode(node.children[0]);
      double d2 = DoubleFromNode(node.children[1]);
      double mult = op(d1,d2);
      node = NodeFactory<Node>::MakeReal(mult);
     return true;
    }
    return false;
};



template <typename Node, typename Op>
Node eval(const Node & node1, const Node & node2, Op op) {
    typedef typename Node::parse_node_t pn_t;
    assert(node1.value.id() == calculator::realID);
    assert(node2.value.id() == calculator::realID);
    double d1 = DoubleFromNode(node1);
    double d2 = DoubleFromNode(node2);
    double mult = op(d1,d2);
    return NodeFactory<Node>::MakeReal(mult);
}

template <typename Node>
Node Negate(const Node & node) {
    assert(node.value.id() == calculator::realID);
    double dval = DoubleFromNode(node);
    return NodeFactory<Node>::MakeReal(-dval);
}

template <class Node>
MarkT GetMark(const Node & node){
    return MarkT(node.value.id(), typename MarkT::value_type(node.value.begin(), node.value.end()));
}

struct MarkTrait {
   template <class Node>
   static MarkT GetMark(const Node & node){
       return ::GetMark(node);
   }
};


template <class Node>
bool IsSameType(const Node & node1, const Node & node2 ) {
  if (node1.value.id() == node2.value.id()){
  std::string mrk1(node1.value.begin(), node1.value.end());
  std::string mrk2(node2.value.begin(), node2.value.end());
  return (mrk1 == mrk2);
  }
}

template <class Node>
bool IsSameType(const Node & node1, const MarkT & mark ) {
  if (node1.value.id() == mark.id ){
  std::string mrk1(node1.value.begin(), node1.value.end());
  return (mrk1 == mark.mark);
  }
}



template<class Node>
void prn(const Node& node){
#ifdef DEBUG
    using std::cout;
    using std::string;

    if (node.children.size()==2) {
        cout<<"(";
        prn(node.children[0]);
        string mrk(node.value.begin(), node.value.end());
        cout<<" "<<mrk<<" ";
        prn(node.children[1]);
        cout<<")";
    } else if (node.children.size()==1) {
        cout<<"(";
        string mrk(node.value.begin(), node.value.end());
        cout<<" "<<mrk<<" ";
        prn(node.children[0]);
        cout<<")";
    } else if (node.children.empty()){
        string mrk(node.value.begin(), node.value.end());
        cout<<""<<mrk<<"";
    } else {
        cout<<"["; 
        unsigned int sz = node.children.size();
        for(unsigned int i = 0; i < sz ; ++i){
	    if (i!= 0) 
                cout<<" ";
            prn(node.children[i]);
        };
        cout<<"]";
    }
#endif
}

////////////////////////////////////////////////
/// strategies
////////////////////////////////////////////////

template <typename Node, typename Fun>
void nbt(Node & node, Fun f)
{
  typedef typename Node::children_t::iterator ch_iter;
  for(ch_iter child = node.children.begin(); child!=node.children.end();++child){
     nbt(*child, f);
  }
  f(node);
  return;
}

////////////////////////////////////////////
/// Other 
/// TODO: move to a lib file
///////////////////////////////////////////

template <typename T>
std::string ToString(const T & t)
{
  std::stringstream str;
  str<<t;
  return str.str();
}

template <class StrT>
void strip(StrT & str){
	if (str.empty())
		return;
	typename StrT::iterator first;
	first = str.begin();
	while(first!=str.end() && !std::isgraph(*first))
		++first;
	str.erase(str.begin(),first);
	if (str.empty())
		return;
	typename StrT::reverse_iterator last = str.rbegin();
	while(last!=str.rend() && !std::isgraph(*last))
		++last;
	str.erase(str.rbegin().base(), last.base());
}

template <class S>
struct LogT {

    LogT(S & o = std::cout):enabled_(true),o_(o){}
    void enable(bool e = true){
	enabled_ = e;
    }
    template<class T1>
    void print_line(const T1& t1){
#ifndef NDEBUG
        if (enabled_)
	    o_<<t1<<std::endl<<std::flush;
#endif
    }

   template<class T1, class T2>
    void  print_line(const T1& t1, const T2& t2){
#ifndef NDEBUG
        if (enabled_) 
	    o_<<t1<<t2<<std::endl<<std::flush;
#endif
    }

    template<class T1, class T2, class T3>
    void print_line(const T1& t1, const T2& t2, const T3& t3){ 
#ifndef NDEBUG
        if (enabled_) 
	    o_<<t1<<t2<<t3<<std::endl<<std::flush;
#endif
    }

private:
    bool enabled_;
    S & o_;
    
};

typedef LogT<std::ostream> Log;

template <typename Ios, typename Str>
bool get_until(Ios & istr, Str & ostr, const typename Str::value_type delim=Str::value_type(';')  ){
    Str str;
	
    typename Str::value_type ch;
    istr.get(ch);
		
    while (ch!=delim && istr) {
        str += ch;
        //if (ch=='\n'){
        //	std::cout<<"> "<<std::flush;
        //}
		istr.get(ch);
    }
    if (ch!=delim)
        str+=ch;
    else if (istr.get(ch) && ch!='\n')
        istr.unget();
    ostr = str;
    if (!istr)
        return false;
    return true;
}

